package org.projectback.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;
import org.projectback.entity.AccountEntity.CustomUser;
import org.projectback.entity.AccountEntity.dataTransferObject.AccountDTO;
import org.projectback.filters.JwtFilters.MainJwtFilter;
import org.projectback.utils.Jwt.JwtProperties;
import org.projectback.utils.Jwt.JwtUtils;
import org.projectback.utils.ResultMessage;
import org.projectback.utils.Utils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


import java.io.IOException;
import java.util.List;


@Slf4j
@Configuration
public class SecurityConfiguration {
    @Resource
    private JwtUtils jwtUtils;
    @Resource
    private Utils utils;
    @Resource
    private JwtProperties jwtProperties;
    @Resource
    private MainJwtFilter mainJwtFilter;
    @Resource()
    private StringRedisTemplate stringRedisTemplate;


    @Bean
    public SecurityFilterChain MyFilterChain(HttpSecurity http) throws Exception {
        return http.
                authorizeHttpRequests(config ->
                        config
                                .requestMatchers("/api/test/refresh").permitAll()
                                .requestMatchers("/api/user/confirmEmail").permitAll()
                                .requestMatchers("/api/user/register").permitAll()
                                .requestMatchers("/api/user/reset").permitAll()
                                .anyRequest()
                                .authenticated()
                )
                //login
                .formLogin(config ->
                        config
                                .loginProcessingUrl("/api/user/login")
                                .successHandler(this::onAuthenticationSuccess)
                                .failureHandler(this::onAuthenticationFailure)
                                .permitAll()
                )
                //logout
                .logout(config ->
                        config
                                .logoutUrl("/api/user/logout")
                                .logoutSuccessHandler(this::onLogoutSuccess)

                )
                //exceptionHandler
                .exceptionHandling(config -> config
                        .authenticationEntryPoint(
                                (request, response, exception) ->
                                {
                                    log.info("拦截请求{}", request.getRequestURI());
                                    response.setCharacterEncoding("UTF-8");
                                    response.getWriter().write(ResultMessage.unauthorized().toJsonString());
                                }
                        )
                        .accessDeniedHandler((request, response, accessDeniedException) -> {
                            response.setCharacterEncoding("UTF-8");
                            response.getWriter().write(ResultMessage.forbidden().toJsonString());
                        })
                )

                //proxy
                .csrf(AbstractHttpConfigurer::disable)
                //session
                .sessionManagement(config -> config.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                //filter
                .addFilterBefore(mainJwtFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    //loginSuccessHandler
    private void onAuthenticationSuccess(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        CustomUser user = (CustomUser) authentication.getPrincipal();
        List<String> userRole = user.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
        String userName = user.getUsername();
        Long id = user.getId();
        //RefreshToken
        String refreshToken = jwtUtils.getRefreshToken(stringRedisTemplate);
        //AccessToken
        String accessToken = jwtUtils.createJwtAccessToken(jwtProperties.getSecretKey(), jwtProperties.getUserTtl(), id, userName, userRole, refreshToken);
        //redis add RefreshToken
        jwtUtils.addRefreshTokenToRedis(stringRedisTemplate, refreshToken, utils.objectToJsonString(user));
        //response
        response.getWriter().write(ResultMessage.success(206, new AccountDTO(accessToken, refreshToken)).toJsonString());
    }

    //login FailureHandler
    private void onAuthenticationFailure(HttpServletRequest request,
                                         HttpServletResponse response,
                                         AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(ResultMessage.failure(402, "账号名或密码错误").toJsonString());
    }

    //logout SuccessfulHandler
    private void onLogoutSuccess(HttpServletRequest request,
                                 HttpServletResponse response,
                                 Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        String token = request.getHeader("Authorization");
        boolean result = deregistrationRefreshToken(stringRedisTemplate, jwtProperties.getSecretKey(), token);
        response.getWriter().write(result ? ResultMessage.success(200, "LogoutSuccess").toJsonString():ResultMessage.unauthorized().toJsonString());
    }

    /**
     * @param stringTemplate 写节点
     * @param secretKey      jwt秘钥
     * @param Token          加密token
     */
    public boolean deregistrationRefreshToken(StringRedisTemplate stringTemplate, String secretKey, String Token) {
        //解密AccessToken
        try {
            //使用token获取uuid 来 验证以及 清空凭证
            Claims claims = jwtUtils.praseJwt(secretKey, Token);
            if (claims != null) {

                String refreshToken = claims.get("refreshToken").toString();

                if (jwtUtils.verifyRefreshToken(stringTemplate, refreshToken)) {

                    return stringTemplate.delete("refreshToken:" + refreshToken);

                } else System.out.println("refreshToken:" + refreshToken + "is null!");
            } else {
                System.out.println("claims is null!");
            }
        }catch (JwtException ignored) {
            //发生异常则返回错误
        }
        return false;
    }

}
